package ess.control;

import java.util.Iterator;
import java.util.Vector;

import ess.hardware.FloorButtonPanel;
import ess.hardware.FloorOperationEvent;
import ess.hardware.FloorOperationEventListener;

public class ElevatorManager implements FloorOperationEventListener, Runnable {
	static ElevatorManager instance = null;
	Vector elevatorProxies;
	Vector floorRequestList;
	private FloorButtonPanel fbp;

	public static ElevatorManager getInstance() {
		if (instance == null) {
			// singleton instance
			instance = new ElevatorManager();
		}
		return instance;
	}

	private ElevatorManager() {
		elevatorProxies = new Vector();
		floorRequestList = new Vector();
		new Thread(this).start();
	}

	public void registerElevator(ess.hardware.Elevator elevator) {
		ElevatorProxy proxy = new ElevatorProxy(elevator);
		elevatorProxies.add(proxy);
		elevator.addArrivalSensorEventListener(proxy);
		elevator.addDoorClosingSensorEventListener(proxy);

	}

	public void operationOccured(FloorOperationEvent event) {

		FloorRequest req = new FloorRequest(event.getOperationFloor(),
				event.getOperationValue());
		ElevatorProxy proxy = getClosestElevator(req.getFloor());
		if (proxy.getPosition() == req.getFloor()
				&& proxy.getDirection() == req.getRequestDirection()
				&& proxy.getCurrentStateType() != ElevatorState.STATE_MOVING) {
			proxy.putCommand(Command.COMMAND_DOOR_OPEN);
			fbp.turnOffButton(req.getFloor(), req.getRequestDirection());
		} else if (proxy.getPosition() == req.getFloor()
				&& proxy.getCurrentStateType() != ElevatorState.STATE_MOVING
				&& proxy.getTargetFloor() == ElevatorProxy.INVALID_TARGET_FLOOR) {
			proxy.changeDirection(Direction.OPPOSITE);
			proxy.putCommand(Command.COMMAND_DOOR_OPEN);
			fbp.turnOffButton(req.getFloor(), req.getRequestDirection());
		} else {
			if (addFloorRequest(req)) {
				synchronized (instance) {
					notify();
				}
			}
		}
	}

	/**
	 * @param requestFloor
	 * @return
	 */
	private ElevatorProxy getClosestElevator(int requestFloor) {
		Iterator<ElevatorProxy> it = elevatorProxies.iterator();
		ElevatorProxy proxy = null;
		ElevatorProxy closestProxy = null;
		//ElevatorProxy idleProxy = null;
		while (it.hasNext()) {
			proxy = it.next();

			if (closestProxy == null) {
				closestProxy = proxy;
			} else {
				System.out.println("proxy:"+proxy.getPosition()+", closest:"+closestProxy.getPosition()+", request:"+requestFloor);
				if (Math.abs(proxy.getPosition() - requestFloor) < Math.abs(closestProxy.getPosition() - requestFloor)) {
					closestProxy = proxy;
				}
			}

			//if (proxy.getCurrentStateType() == ElevatorState.STATE_CLOSED) {
			//	idleProxy = proxy;
			//}
		}

		//if (closestProxy.getCurrentStateType() != ElevatorState.STATE_CLOSED && idleProxy != null)
		//	closestProxy = idleProxy;
		System.out.println("getClosestElevator id:" + closestProxy.getId());
		return closestProxy;
	}

	private ElevatorProxy getInCommingElevator(int requestFloor, int direction) {
		Iterator<ElevatorProxy> it = elevatorProxies.iterator();
		ElevatorProxy proxy = null;
		while (it.hasNext()) {
			proxy = it.next();
			if (proxy.getCurrentStateType() != ElevatorState.STATE_CLOSED
					&& proxy.getDirection() == direction) {
				if ((direction == Direction.UP && proxy.getPosition() < requestFloor)
						|| (direction == Direction.DOWN && proxy.getPosition() > requestFloor)) {
					return proxy;
				}
			}
		}
		return null;
	}

	private FloorRequest getNotAssignedFloorRequest() {
		Iterator<FloorRequest> it = floorRequestList.iterator();
		FloorRequest req = null;
		while (it.hasNext()) {
			req = (FloorRequest) it.next();
			if (req.isAssigned() == false)
				return req;
		}
		return null;
	}

	public FloorRequest getFloorRequestAssigned(int elevatorId) {
		Iterator<FloorRequest> it = floorRequestList.iterator();
		FloorRequest req = null;
		while (it.hasNext()) {
			req = (FloorRequest) it.next();
			if (req.isAssigned() == true
					&& req.getAssignedElevatorId() == elevatorId
					) {
				return req;
			}
		}
		return null;
	}
	
	public FloorRequest getFloorRequestAssigned(int elevatorId, int floor) {
		Iterator<FloorRequest> it = floorRequestList.iterator();
		FloorRequest req = null;
		while (it.hasNext()) {
			req = (FloorRequest) it.next();
			if (req.isAssigned() == true
					&& req.getAssignedElevatorId() == elevatorId &&
					req.getFloor() == floor
					) {
				return req;
			}
		}
		return null;
	}

	public void run() {
		for (;;) {
			FloorRequest req = getNotAssignedFloorRequest();

			while (req == null) {

				try {
					synchronized (instance) {
						wait();
					}
					req = getNotAssignedFloorRequest();

				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			int requestFloor = req.getFloor();
			ElevatorProxy proxy = null;

			//proxy = getInCommingElevator(requestFloor, req.getRequestDirection());
			
			if (proxy == null){
				System.out.println("get in comming is null");
				proxy = getClosestElevator(requestFloor);
			}

			req.setAssignedElevator(proxy.getId());

			if (proxy.getCurrentStateType() == ElevatorState.STATE_CLOSED
					&& proxy.getTargetFloor() == ElevatorProxy.INVALID_TARGET_FLOOR) {
				if (requestFloor > proxy.getPosition()) {
					proxy.changeDirection(Direction.UP);
					proxy.putCommand(Command.COMMAND_MOVE);
				} else if (requestFloor < proxy.getPosition()) {
					proxy.changeDirection(Direction.DOWN);
					proxy.putCommand(Command.COMMAND_MOVE);
				}
			}
		}

	}

	public ElevatorProxy getElevatorProxy(int id) {
		Iterator<ElevatorProxy> it = elevatorProxies.iterator();
		ElevatorProxy proxy = null;
		while (it.hasNext()) {
			proxy = it.next();
			if (proxy.getId() == id) {
				return proxy;
			}
		}
		return null;
	}

	private boolean addFloorRequest(FloorRequest req) {
		if (!floorRequestList.contains(req)) {
			floorRequestList.add(req);
			// TODO: turn on floor button
			fbp.turnOnButton(req.getFloor(), req.getRequestDirection());
			return true;
		}
		return false;
	}

	private boolean removeFloorRequest(FloorRequest req) {
		if (floorRequestList.contains(req)) {
			if (floorRequestList.remove(req)) {
				if (fbp != null) {
					System.out.println("removeFloorRequest floor:"
							+ req.getFloor() + " dir:"
							+ req.getRequestDirection());
					fbp.turnOffButton(req.getFloor(), req.getRequestDirection());
				}
				return true;
			}
		}
		return false;
	}

	public void notifyElevatorArrived(ElevatorProxy proxy, int floor) {
		if (proxy.getTargetFloor() == ElevatorProxy.INVALID_TARGET_FLOOR) {
			FloorRequest assignedReq = getFloorRequestAssigned(proxy.getId(), floor);

			if (assignedReq == null)
				return;
			//if (floor != assignedReq.getFloor())
			//	return;

			if (assignedReq.getRequestDirection() == proxy.getDirection()) {
				removeFloorRequest(assignedReq);
				proxy.putCommand(Command.COMMAND_STOP);
			} else {
				int oppositeDirection = proxy.getDirection() == Direction.UP ? Direction.DOWN
						: Direction.UP;
				if (assignedReq.getRequestDirection() == oppositeDirection) {
					if (removeFloorRequest(new FloorRequest(floor,
							oppositeDirection))) {
						proxy.putCommand(Command.COMMAND_STOP);
						proxy.changeDirection(oppositeDirection);
					}
				}
			}

		} else if (removeFloorRequest(new FloorRequest(floor,
				proxy.getDirection()))) {
			proxy.putCommand(Command.COMMAND_STOP);
		}
	}

	public void registerFloorButtonPanel(FloorButtonPanel fbp) {
		this.fbp = fbp;
	}

	boolean hasMoreFloorRequest() {
		return floorRequestList.isEmpty() == false;
	}
}
